`timescale 1ns/1ps

module uart_transceiver_tb;

    parameter CLK_FREQ   = 100_000_000;
    parameter BAUD_RATE  = 115200;
    parameter CLK_PERIOD = 10;  // 100MHz -> 10ns
    parameter MUX_NUM    = 16;  // 为了仿真快一点，用16个

    reg clk;
    reg rst_n;
    reg rx;
    wire tx;

    // BRAM 仿真
    reg [15:0] bram [0:MUX_NUM-1];
    wire [15:0] bram_r_data;
    wire [$clog2(MUX_NUM)-1:0] bram_r_addr;
    wire bram_r_en;

    // DUT
    wire busy;
    wire [$clog2(MUX_NUM)-1:0] mux_index;

    uart_transceiver #(
        .mux_num(MUX_NUM)
    ) dut (
        .clk(clk),
        .rst_n(rst_n),
        .rx(rx),
        .tx(tx),
        .result_bram_r_data(bram_r_data),
        .result_bram_r_addr(bram_r_addr),
        .result_bram_r_en(bram_r_en),
        .busy(busy),
        .mux_index(mux_index)
    );

    assign bram_r_data = bram[bram_r_addr];

    // 时钟生成
    initial clk = 0;
    always #(CLK_PERIOD/2) clk = ~clk;

    // 初始化 BRAM
    integer i;
    initial begin
        for (i=0; i<MUX_NUM; i=i+1) begin
            bram[i] = i + 16'h1000;  // 递增数据
        end
    end

    // 复位
    initial begin
        rst_n = 0;
        rx = 1; // 空闲高电平
        #(CLK_PERIOD*20);
        rst_n = 1;
    end

    // 注入 UART 数据 "r" (8'h72)
    initial begin
        #(CLK_PERIOD*200); // 等待系统稳定
        send_byte(8'h72);  // 发送字符 'r'
    end

    // 模拟 UART RX 发送一个字节
    task send_byte(input [7:0] data);
        integer j;
        integer bit_time;
        begin
            bit_time = CLK_FREQ / BAUD_RATE;
            // 起始位
            rx <= 0;
            #(bit_time*CLK_PERIOD);
            // 数据位
            for (j=0; j<8; j=j+1) begin
                rx <= data[j];
                #(bit_time*CLK_PERIOD);
            end
            // 停止位
            rx <= 1;
            #(bit_time*CLK_PERIOD);
        end
    endtask

    // 监控 TX 输出（解析 UART 字节）
    initial begin
        forever begin
            wait(tx == 0);  // 起始位
            sample_uart_byte();
        end
    end

    task sample_uart_byte;
        integer k;
        integer bit_time;
        reg [7:0] rxed;
        begin
            bit_time = CLK_FREQ / BAUD_RATE;
            #(bit_time*CLK_PERIOD + (bit_time/2)*CLK_PERIOD); // 到第一个数据位中点
            for (k=0; k<8; k=k+1) begin
                rxed[k] = tx;
                #(bit_time*CLK_PERIOD);
            end
            // 停止位时间
            #(bit_time*CLK_PERIOD);
            $display("Time %0t ns: TX sent byte = 0x%02h (%0d)", $time, rxed, rxed);
        end
    endtask

    // 仿真超时
    initial begin
        #(CLK_PERIOD*1_000_000);
        $display("Simulation timeout");
        $finish;
    end

endmodule
